package com.puff.web.mvc;

import java.io.UnsupportedEncodingException;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import com.puff.core.Puff;
import com.puff.framework.container.ExecutorContainer;

public class PuffExecutorProvider implements ExecutorProvider {
	private static final String encoding = Puff.getEncoding();

	@Override
	public Executor getExecutor(HttpServletRequest request, String requestURI) {
		List<Executor> executors = ExecutorContainer.get(requestURI);
		String requestMethod = request.getMethod();
		if (executors == null || executors.size() == 0) {
			Executor result = null;
			int i = requestURI.lastIndexOf("/");
			boolean hasDynamic = ExecutorContainer.hasDynamic();
			if (i != -1) {
				String uri = i == 0 ? "/" : requestURI.substring(0, i);
				executors = ExecutorContainer.get(uri);
				result = findExecutor(requestURI, executors, requestMethod, i);
				if (result == null && hasDynamic) {
					result = getDynamicExecutor(requestMethod, requestURI);
				}
			} else {
				if (hasDynamic) {
					result = getDynamicExecutor(requestMethod, requestURI);
				}
			}
			return result;
		} else if (executors.size() == 1) {
			Executor executor = executors.get(0);
			if (canInvoke(executor.requestMethod, requestMethod)) {
				return executor;
			}
		} else {
			for (Executor executor : executors) {
				if (canInvoke(executor.requestMethod, requestMethod)) {
					return executor;
				}
			}
		}
		return null;
	}

	private Executor getDynamicExecutor(String requestMethod, String requestURI) {
		String[] arr = requestURI.split("/");
		List<Executor> dynamics = ExecutorContainer.getDynamic(arr.length - 1);
		if (dynamics != null && dynamics.size() > 0) {
			int size = dynamics.size();
			if (size == 1) {
				Executor executor = dynamics.get(0);
				if (canInvoke(executor.requestMethod, requestMethod)) {
					StringBuilder param = new StringBuilder(64);
					int[] paramIdx = executor.getParamIdx();
					for (int j = 0; j < paramIdx.length;) {
						param.append(arr[paramIdx[j++]]);
						if (j < paramIdx.length) {
							param.append(Puff.getUrlParamSeparator());
						}
					}
					PuffContext.setUrlParam(param.toString());
					return executor;
				}
			} else {
				each: for (Executor executor : dynamics) {
					if (!canInvoke(executor.requestMethod, requestMethod)) {
						continue;
					}
					String[] compare = executor.getCompare();
					int[] compareIdx = executor.getCompareIdx();
					for (int i = 0; i < compare.length;) {
						String m = compare[i];
						if (!m.equals(arr[compareIdx[i]])) {
							continue each;
						}
						if (++i == compare.length) {
							// 判断成功 提取参数
							StringBuilder param = new StringBuilder(64);
							int[] paramIdx = executor.getParamIdx();
							for (int j = 0; j < paramIdx.length;) {
								param.append(arr[paramIdx[j++]]);
								if (j < paramIdx.length) {
									param.append(Puff.getUrlParamSeparator());
								}
							}
							PuffContext.setUrlParam(param.toString());
							return executor;
						}
					}
				}
			}
		}
		return null;
	}

	private Executor findExecutor(String requestURI, List<Executor> executors, String requestMethod, int i) {
		if (executors == null || executors.size() == 0) {
			return null;
		} else if (executors.size() == 1) {
			Executor executor = executors.get(0);
			if (canInvoke(executor.requestMethod, requestMethod)) {
				PuffContext.setUrlParam(extractUrlParam(requestURI, i));
				return executor;
			}
		} else {
			for (Executor executor : executors) {
				if (canInvoke(executor.requestMethod, requestMethod)) {
					PuffContext.setUrlParam(extractUrlParam(requestURI, i));
					return executor;
				}
			}
		}
		return null;
	}

	/**
	 * 提取URL参数
	 */
	private String extractUrlParam(String requestURI, int i) {
		String urlParam = null;
		try {
			urlParam = new String(requestURI.substring(i + 1).getBytes("ISO-8859-1"), encoding);
		} catch (UnsupportedEncodingException e) {
		}
		return urlParam;
	}

	protected boolean canInvoke(String executorMethod, String requestMethod) {
		return "ALL".equals(executorMethod) || requestMethod.equals(executorMethod);
	}

}
